"""
Cherry-pick PR commits 命令 - 查询 PR 的 commits 并 cherry-pick
"""
import os
import click
import subprocess
from pathlib import Path
from typing import Tuple
from urllib.parse import urlparse
from gitcode_client import GitCodeClient
from config import Config
from repo_manager import RepoManager
from i18n import t


def parse_pr_specs(specs_str: str) -> list:
    """
    解析 PR 规格字符串
    格式: repo#pr_number,repo#pr_number
    例如: cangjie_compiler#35,cangjie_test#24
    
    :param specs_str: PR 规格字符串
    :return: [(repo, pr_number), ...] 列表
    """
    specs = []
    parts = [p.strip() for p in specs_str.split(',') if p.strip()]
    
    for part in parts:
        if '#' not in part:
            click.echo(
                t("cherry.warning_invalid_format_part_expected_repo", part=part),
                err=True,
            )
            continue
        
        repo, pr_str = part.rsplit('#', 1)
        repo = repo.strip()
        try:
            pr_number = int(pr_str.strip())
            specs.append((repo, pr_number))
        except ValueError:
            click.echo(
                t("cherry.warning_invalid_pr_number_pr_str", pr_str=pr_str, part=part),
                err=True,
            )
    
    return specs


def fetch_pr_commits(client: GitCodeClient, owner: str, repo: str, pr_number: int) -> list:
    """
    获取 PR 的 commits
    :param client: GitCodeClient 实例
    :param owner: 仓库所有者
    :param repo: 仓库名
    :param pr_number: PR 编号
    :return: commits 列表
    """
    success, commits = client.get_pr_commits(owner, repo, pr_number)
    
    if not success:
        error_msg = commits[0].get('error', 'Unknown error') if commits else 'Unknown error'
        click.echo(
            t("cherry.failed_to_fetch_commits_for_repo", repo=repo, pr_number=pr_number, error_msg=error_msg),
            err=True,
        )
        return []
    
    return commits


def _build_fetch_url(config: Config, owner: str, repo: str) -> str:
    """
    根据配置构建 HTTPS fetch URL，格式: https://oauth2:token@host/owner/repo.git
    """
    token = os.getenv("gitcode_password") or os.getenv("gitcode_token")
    if not token:
        raise click.ClickException(t("cherry.missing_gitcode_token_please_set_gitcode"))

    base_url = config.get_base_url() or "https://gitcode.com"
    parsed = urlparse(base_url if "://" in base_url else f"https://{base_url.lstrip('/')}")
    host = parsed.netloc or parsed.path.strip('/').split('/')[0] or "gitcode.com"
    repo_owner = owner or config.get_base_org() or "Cangjie"
    return f"https://oauth2:{token}@{host}/{repo_owner}/{repo}.git"


def fetch_pr_branch(repo_path: Path, repo: str, owner: str, pr_number: int, config: Config) -> Tuple[bool, str]:
    """
    使用命令 `git fetch <url> +refs/merge-requests/<id>/head:pr_<id>` 获取 PR HEAD。
    返回 (是否成功, 本地分支名)。
    """
    local_branch = f"pr_{pr_number}"
    repo_url = _build_fetch_url(config, owner, repo)

    subprocess.run(
        ["git", "branch", "-D", local_branch],
        cwd=repo_path,
        capture_output=True,
        text=True
    )

    fetch_refspec = f"+refs/merge-requests/{pr_number}/head:{local_branch}"
    display_url = repo_url
    token = os.getenv("gitcode_password") or os.getenv("gitcode_token")
    if token:
        display_url = repo_url.replace(token, "******")
    click.echo(t("cherry.running_git_fetch_url_refspec", url=display_url, refspec=fetch_refspec))
    result = subprocess.run(
        ["git", "fetch", repo_url, fetch_refspec],
        cwd=repo_path,
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        click.echo(
            t("cherry.failed_to_fetch_pr_pr_number", pr_number=pr_number, error=result.stderr.strip()),
            err=True,
        )
        return False, local_branch

    click.echo(t("cherry.pr_head_fetched_into_local_branch", branch=local_branch))
    return True, local_branch


def cherry_pick_commits(repo_path: Path, commit_shas: list) -> bool:
    """
    在指定仓库中 cherry-pick 一系列 commits
    :param repo_path: 仓库路径
    :param commit_shas: commit SHA 列表
    :return: 是否全部成功
    """
    if not repo_path.exists():
        click.echo(t("cherry.repository_path_does_not_exist_path", path=repo_path), err=True)
        return False
    
    if not commit_shas:
        click.echo(t("cherry.no_commits_to_cherry_pick"))
        return True
    
    # 确保工作目录干净
    status_result = subprocess.run(
        ["git", "status", "--porcelain"],
        cwd=repo_path,
        capture_output=True,
        text=True
    )
    if status_result.stdout.strip():
        click.echo(t("cherry.warning_repository_has_uncommitted_changes_please"), err=True)
        return False
    
    all_success = True
    for i, sha in enumerate(commit_shas):
        click.echo(
            t("cherry.cherry_picking_sha_index_total", sha=sha[:8], index=i + 1, total=len(commit_shas)),
            nl=False,
        )
        cmd = ["git", "cherry-pick", "--allow-empty", sha]
        result = subprocess.run(
            cmd,
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        if result.returncode == 0:
            click.echo(" ✓")
        else:
            click.echo(" ✗")
            
            # 检查是否是冲突
            is_conflict = False
            status_result = subprocess.run(
                ["git", "status", "--porcelain"],
                cwd=repo_path,
                capture_output=True,
                text=True
            )
            if "UU" in status_result.stdout or "AA" in status_result.stdout or "DD" in status_result.stdout:
                is_conflict = True
                click.echo(t("cherry.merge_conflict_detected"))
                click.echo(t("cherry.conflicted_files"))
                conflict_files = []
                for line in status_result.stdout.strip().split('\n'):
                    if line and (line.startswith('UU') or line.startswith('AA') or line.startswith('DD')):
                        conflict_files.append(line[3:].strip())
                for file in conflict_files:
                    click.echo(t("cherry.value", value=file))
            else:
                click.echo(t("cherry.error_message", message=result.stderr.strip()))
            
            all_success = False
            
            # 提供冲突处理选项
            if is_conflict:
                click.echo(t("cherry.n_conflict_resolution_options"))
                click.echo(t("cherry.1_resolve_conflicts_manually_you_ll"))
                click.echo(t("cherry.2_skip_this_commit_and_continue"))
                click.echo(t("cherry.3_abort_all_cherry_picks_and"))
                
                while True:
                    choice = click.prompt(t("cherry.choose_an_option_1_3"), type=int, default=1)
                    if choice == 1:
                        # 用户手动解决冲突
                        click.echo(t("cherry.n_to_resolve_conflicts_manually"))
                        click.echo(t("cherry.1_edit_the_conflicted_files_to"))
                        click.echo(t("cherry.2_run_git_add_resolved_files"))
                        click.echo(t("cherry.3_run_git_cherry_pick_continue"))
                        click.echo(t("cherry.or_to_abort_git_cherry_pick"))
                        click.echo(t("cherry.n_waiting_for_you_to_resolve"))
                        click.echo(t("cherry.this_script_will_check_if_conflicts"))
                        
                        # 循环检查直到冲突解决或用户选择 abort
                        max_attempts = 10
                        attempt = 0
                        while attempt < max_attempts:
                            attempt += 1
                            if click.confirm(t("cherry.have_you_resolved_the_conflicts_attempt", attempt=attempt, max_attempts=max_attempts)):
                                # 检查是否还有冲突标记
                                status_check = subprocess.run(
                                    ["git", "status", "--porcelain"],
                                    cwd=repo_path,
                                    capture_output=True,
                                    text=True
                                )
                                
                                has_conflicts = "UU" in status_check.stdout or "AA" in status_check.stdout or "DD" in status_check.stdout
                                
                                if not has_conflicts:
                                    # 尝试继续 cherry-pick
                                    continue_result = subprocess.run(
                                        ["git", "cherry-pick", "--continue"],
                                        cwd=repo_path,
                                        capture_output=True,
                                        text=True
                                    )
                                    
                                    if continue_result.returncode == 0:
                                        click.echo(t("cherry.conflict_resolved_continuing"))
                                        break
                                    else:
                                        click.echo(t("cherry.failed_to_continue_error", error=continue_result.stderr.strip()))
                                        if click.confirm(t("cherry.abort_this_cherry_pick")):
                                            subprocess.run(
                                                ["git", "cherry-pick", "--abort"],
                                                cwd=repo_path,
                                                capture_output=True,
                                                text=True
                                            )
                                            return False
                                else:
                                    click.echo(t("cherry.conflicts_still_exist_in_files_please"))
                                    if click.confirm(t("cherry.abort_this_cherry_pick_instead")):
                                        subprocess.run(
                                            ["git", "cherry-pick", "--abort"],
                                            cwd=repo_path,
                                            capture_output=True,
                                            text=True
                                        )
                                        return False
                            else:
                                # 用户选择 abort
                                if click.confirm(t("cherry.abort_this_cherry_pick")):
                                    subprocess.run(
                                        ["git", "cherry-pick", "--abort"],
                                        cwd=repo_path,
                                        capture_output=True,
                                        text=True
                                    )
                                    click.echo(t("cherry.aborted_cherry_pick_for_this_commit"))
                                    return False
                                # 否则继续等待
                        
                        if attempt >= max_attempts:
                            click.echo(t("cherry.maximum_attempts_reached_aborting"))
                            subprocess.run(
                                ["git", "cherry-pick", "--abort"],
                                cwd=repo_path,
                                capture_output=True,
                                text=True
                            )
                            return False
                        
                        break
                    elif choice == 2:
                        # 跳过当前 commit
                        # 先尝试 abort（如果还在 cherry-pick 状态）
                        subprocess.run(
                            ["git", "cherry-pick", "--abort"],
                            cwd=repo_path,
                            capture_output=True,
                            text=True
                        )
                        click.echo(t("cherry.skipped_this_commit_continuing"))
                        break
                    elif choice == 3:
                        # 中止所有 cherry-pick
                        subprocess.run(
                            ["git", "cherry-pick", "--abort"],
                            cwd=repo_path,
                            capture_output=True,
                            text=True
                        )
                        click.echo(t("cherry.aborted_all_cherry_picks"))
                        return False
                    else:
                        click.echo(t("cherry.invalid_choice_please_enter_1_2"))
            else:
                # 非冲突错误，询问是否继续
                click.echo(t("cherry.options"))
                click.echo(t("cherry.1_skip_this_commit_and_continue"))
                click.echo(t("cherry.2_abort_all_cherry_picks_and"))
                
                while True:
                    choice = click.prompt(t("cherry.choose_an_option_1_2"), type=int, default=2)
                    if choice == 1:
                        # 跳过当前 commit（如果可能）
                        skip_result = subprocess.run(
                            ["git", "cherry-pick", "--skip"],
                            cwd=repo_path,
                            capture_output=True,
                            text=True
                        )
                        if skip_result.returncode == 0:
                            click.echo(t("cherry.skipped_this_commit_continuing"))
                        else:
                            # 如果不能 skip，尝试 abort
                            subprocess.run(
                                ["git", "cherry-pick", "--abort"],
                                cwd=repo_path,
                                capture_output=True,
                                text=True
                            )
                            click.echo(t("cherry.aborted_cherry_pick_for_this_commit"))
                        break
                    elif choice == 2:
                        # 中止所有 cherry-pick
                        subprocess.run(
                            ["git", "cherry-pick", "--abort"],
                            cwd=repo_path,
                            capture_output=True,
                            text=True
                        )
                        click.echo(t("cherry.aborted_all_cherry_picks"))
                        return False
                    else:
                        click.echo(t("cherry.invalid_choice_please_enter_1_or"))
    
    return all_success


@click.command()
@click.option('--specs', '-s', required=True, 
              help='PR 规格，格式: repo#pr_number,repo#pr_number (例如: cangjie_compiler#35,cangjie_test#24)')
@click.option('--owner', '-o', help='仓库所有者（默认使用配置中的 base_org）')
@click.option('--dry-run', is_flag=True, help='只显示将要 cherry-pick 的 commits，不实际执行')
def cherry_pick(specs, owner, dry_run):
    """
    查询 PR 的 commits 并 cherry-pick 到本地仓库
    
    从指定的 PR 中获取所有 commits，然后按顺序 cherry-pick 到对应的本地仓库。
    
    示例:
        cjdev cherry-pick -s cangjie_compiler#35,cangjie_test#24
    """
    config = Config()
    client = GitCodeClient()
    repo_manager = RepoManager(config)
    
    # 确定 owner
    if not owner:
        owner = config.get_base_org()
    
    # 解析 PR 规格
    pr_specs = parse_pr_specs(specs)
    if not pr_specs:
        click.echo(t("cherry.error_no_valid_pr_specifications_found"), err=True)
        return
    
    click.echo(t("cherry.found_count_pr_specification_s", count=len(pr_specs)))
    for repo, pr_number in pr_specs:
        click.echo(t("cherry.repo_pr_number", repo=repo, pr_number=pr_number))
    
    # 获取每个 PR 的 commits
    click.echo(t("cherry.nfetching_commits_from_prs"))
    pr_commits_map = {}
    
    for repo, pr_number in pr_specs:
        click.echo(t("cherry.nfetching_commits_for_repo_pr_number", repo=repo, pr_number=pr_number))
        commits = fetch_pr_commits(client, owner, repo, pr_number)
        
        if commits:
            pr_commits_map[(repo, pr_number)] = commits
            click.echo(t("cherry.found_count_commit_s", count=len(commits)))
            for commit in commits:
                sha = commit.get('sha', 'unknown')
                message = commit.get('commit', {}).get('message', 'No message')
                first_line = message.split('\n')[0] if message else 'No message'
                click.echo(t("cherry.sha_message", sha=sha[:8], message=first_line))
        else:
            click.echo(t("cherry.no_commits_found_or_failed_to"))
    
    if not pr_commits_map:
        click.echo(t("cherry.nno_commits_found_in_any_pr"))
        return
    
    # 如果是 dry-run，只显示信息并退出
    if dry_run:
        click.echo("\n" + "="*70)
        click.echo(t("cherry.dry_run_commits_that_would_be"))
        click.echo("="*70)
        for (repo, pr_number), commits in pr_commits_map.items():
            repo_path = repo_manager.get_repo_path(repo)
            click.echo(t("cherry.n_repo_pr_number", repo=repo, pr_number=pr_number))
            if repo_path.exists():
                click.echo(t("cherry.repository_path_path", path=repo_path))
                for commit in commits:
                    sha = commit.get('sha', 'unknown')
                    message = commit.get('commit', {}).get('message', 'No message')
                    first_line = message.split('\n')[0] if message else 'No message'
                    click.echo(t("cherry.sha_message", sha=sha[:8], message=first_line))
            else:
                click.echo(t("cherry.repository_not_found_at_path", path=repo_path))
        click.echo("\n" + "="*70)
        return
    
    # 确认是否继续
    click.echo("\n" + "="*70)
    click.echo(t("cherry.summary_of_commits_to_cherry_pick"))
    click.echo("="*70)
    for (repo, pr_number), commits in pr_commits_map.items():
        repo_path = repo_manager.get_repo_path(repo)
        click.echo(t("cherry.n_repo_pr_number", repo=repo, pr_number=pr_number))
        if repo_path.exists():
            click.echo(t("cherry.repository_path", path=repo_path))
            click.echo(t("cherry.commits_count", count=len(commits)))
            for commit in commits:
                sha = commit.get('sha', 'unknown')
                message = commit.get('commit', {}).get('message', 'No message')
                first_line = message.split('\n')[0] if message else 'No message'
                click.echo(t("cherry.sha_message", sha=sha[:8], message=first_line))
        else:
            click.echo(t("cherry.repository_not_found_at_path", path=repo_path))
    
    if not click.confirm(t("cherry.nproceed_with_cherry_picking_these_commits")):
        click.echo(t("common.cancelled"))
        return
    
    # 执行 cherry-pick
    click.echo(t("cherry.ncherry_picking_commits"))
    results = {}
    
    for (repo, pr_number), commits in pr_commits_map.items():
        repo_path = repo_manager.get_repo_path(repo)
        
        if not repo_path.exists():
            click.echo(t("cherry.n_repo_repository_not_found_at", repo=repo, path=repo_path))
            click.echo(t("cherry.use_cjdev_init_and_cjdev_sync"))
            results[(repo, pr_number)] = False
            continue
        
        click.echo(t("cherry.n_repo_pr_number", repo=repo, pr_number=pr_number))
        commit_shas = [commit.get('sha') for commit in commits if commit.get('sha')]
        
        if not commit_shas:
            click.echo(t("cherry.no_valid_commit_shas_found"))
            results[(repo, pr_number)] = False
            continue
        
        # 通过 refs/merge-requests/<id>/head 获取 PR HEAD
        fetch_success, local_branch = fetch_pr_branch(repo_path, repo, owner, pr_number, config)
        if not fetch_success:
            results[(repo, pr_number)] = False
            continue
        
        # 执行 cherry-pick
        success = cherry_pick_commits(repo_path, commit_shas)
        
        # 清理本地临时分支
        subprocess.run(
            ["git", "branch", "-D", local_branch],
            cwd=repo_path,
            capture_output=True,
            text=True
        )
        
        results[(repo, pr_number)] = success
        
        if success:
            click.echo(t("cherry.successfully_cherry_picked_count_commit_s", count=len(commit_shas)))
        else:
            click.echo(t("cherry.failed_to_cherry_pick_some_commits"))
    
    # 显示最终结果
    click.echo("\n" + "="*70)
    click.echo(t("cherry.summary"))
    click.echo("="*70)
    
    success_count = sum(1 for success in results.values() if success)
    total_count = len(results)
    
    for (repo, pr_number), success in results.items():
        status = "✓" if success else "✗"
        click.echo(t("cherry.status_repo_pr_number", status=status, repo=repo, pr_number=pr_number))
    
    click.echo(t("cherry.ntotal_success_total_pr_s_successfully", success=success_count, total=total_count))
    click.echo("="*70)

